using System;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using System.Threading;

/*
 * Created by SharpDevelop.
 * User: Administrator
 * Date: 6/8/2009
 * Time: 12:15 AM
 * 
 * To change this template use Tools | Options | Coding | Edit Standard Headers.
 */
 
namespace Communication
{
	public interface ObjectPipe<T> where T: ISerializable
    {
    	T getObject();
    	void pushObject(string receiver, T datum);
    }
	
	public interface IActor<T> where T: ISerializable
    {
        IActor<T> newActor(ObjectPipe<T> op);
        void Act();
    }
    
	namespace Network
	{
		public class Server<T> where T: class,ISerializable
	    {
	    	private Listener<T> listener;
	        private Thread listenerThread;
	
	        public Server(int port, IActor<T> actor)
	        {
	        	listener = new Listener<T>(port, actor);
	            listenerThread = new Thread(new ThreadStart(listener.Run));
	            listenerThread.Start();
	        }
	
	        public void Stop()
	        {
	        	listenerThread.Abort();
	        }
	    }
	
	    internal class Listener<T> where T: class,ISerializable
	    {
	        private int serverPort;
	        private IActor<T> actor;
	
	        public Listener(int port, IActor<T> actor)
	        {
	            this.serverPort = port;
	            this.actor = actor;
	        }
	
	        /// <summary>
	        /// Runs this instance.
	        /// </summary>
	        public void Run()
	        {
	            try
	            {
	                TcpListener listener = new TcpListener(IPAddress.Parse("127.0.0.1"), serverPort);
	                listener.Start();
	                while (true)
	                {
	                    if (listener.Pending())
	                    {                        
	                        asyncTransfer<T> trans = new asyncTransfer<T>(listener.AcceptTcpClient());
	                        IActor<T> newActor = actor.newActor(trans);
	                        (new Thread(new ThreadStart(newActor.Act))).Start();
	                    }
	                    else
	                    {
	                        Thread.Sleep(1000);
	                    }
	                }
	            }
	            catch (Exception ex)
	            {
	                //Console.WriteLine(ex.Message);
	            }
	        }
	    }
	    
		public class asyncTransfer<T> : ObjectPipe<T> where T:class,ISerializable
	    {
	    	
	    	private volatile T finalObject;	    	
	    	private Thread aT;
	    	
	    	//Send only
	    	public asyncTransfer()
	    	{
	    	}
	    	
	    	public asyncTransfer(TcpClient tclient)
	    	{
	    		asyncReceiver aR = new asyncReceiver(tclient, this);
	    		aT = new Thread(new ThreadStart(aR.Receive));
	            aT.Start();	            
	    	}
		    
	    	private class asyncReceiver
	    	{
	    		TcpClient tClient;
	    		asyncTransfer<T> parent;
	    		
	    		public asyncReceiver(TcpClient tclient, asyncTransfer<T> parent)
	    		{
	    			this.tClient = tclient;
	    			this.parent = parent;
	    		}
	    		
		    	public void Receive()
		    	{
		    		Stream s = tClient.GetStream();
		    		BinaryFormatter bf = new BinaryFormatter();
		    		try{
		    		parent.finalObject = (T)bf.Deserialize(s);
		    		}
		    		catch(Exception e)
		    		{
		    			Console.WriteLine(e.ToString());
		    		}
		    		s.Close();
		    	}
	    	}
	    		    	
			public T getObject()
			{
				aT.Join();
				return finalObject;
			}
			
			public void pushObject(string receiver, T datum)
			{
				pushObj(receiver, datum);
			}
			
			private static void pushObj(string receiver, T datum)
			{
				string[] sA = receiver.Split(new char[]{':'});
	    		asyncPusher ap = new asyncPusher(sA[0], int.Parse(sA[1]), datum);
	    		var pT = new Thread(new ThreadStart(ap.asyncPush));
	            pT.Start();
			}
			
			private class asyncPusher
			{
				string ip;
				int port;
				T o;
				public asyncPusher(string ip, int port, T o)
				{
					this.ip = ip;
					this.port = port;
					this.o = o;
				}
				
				public void asyncPush()
		    	{
					TcpClient tc = new TcpClient();
					for(int i=0; !tc.Connected && i<200; i++)
					{						
						try {
								tc.Connect(ip, port);
							}
							catch
							{
								Thread.Sleep(200);
							}
					}
					Stream s = tc.GetStream();
					
					BinaryFormatter bf = new BinaryFormatter();
					bf.Serialize(s,o);
					s.Flush();
					tc.Close();
		    	}
			}
	    }
	}
}
